package com.dayuanit.dy15.atm.service.impl;

import com.dayuanit.dy15.atm.compartor.CardCompartor;
import com.dayuanit.dy15.atm.dto.CardDTO;
import com.dayuanit.dy15.atm.entity.Card;
import com.dayuanit.dy15.atm.entity.Flow;
import com.dayuanit.dy15.atm.enums.CardEnum;
import com.dayuanit.dy15.atm.enums.FlowTypeEnum;
import com.dayuanit.dy15.atm.exception.BizException;
import com.dayuanit.dy15.atm.mapper.CardMapper;
import com.dayuanit.dy15.atm.mapper.FlowMapper;
import com.dayuanit.dy15.atm.service.CardService;
import com.dayuanit.dy15.atm.util.CardUtil;
import com.dayuanit.dy15.atm.util.MoneyUtil;
import javafx.scene.layout.BackgroundImage;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
public class CardServiceImpl implements CardService {

    private static final Logger logger = LoggerFactory.getLogger(CardServiceImpl.class);

    @Autowired
    protected CardMapper cardMapper;

    @Autowired
    protected FlowMapper flowMapper;

    @Override
    public void openaccount(String password, String confirmPwd, long userId) {
        logger.info("开户 password=", password);

        if (StringUtils.isBlank(password) ||
            StringUtils.isBlank(confirmPwd)) {
            throw new BizException("有必填参数为空");
        }

        if (!password.equals(confirmPwd)) {
            throw new BizException("两次密码不相等");
        }

        String cardNum = null;
        Card card = null;

        //方案一：
//        while(true) {
//            cardNum = CardUtil.createCardNum();
//            Card card = cardMapper.getByCardNum(cardNum);
//            if (null == card) {
//                break;
//            }
//        }

        //方案二：
        do {
            cardNum = CardUtil.createCardNum();
            card = cardMapper.getByCardNum(cardNum);
        } while (card != null);

        card = new Card();
        card.setBalance(0);
        card.setUserId(userId);
        card.setStatus(CardEnum.available.getK());
        card.setPassword(password);
        card.setModifyTime(new Date());
        card.setCreateTime(card.getModifyTime());
        card.setCardNum(cardNum);

        cardMapper.insert(card);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deposit(long cardId, String password, String amount, long userId) {
        if (StringUtils.isBlank(password) ||
            StringUtils.isBlank(amount)) {
            throw new BizException("有必填参数为空");
        }

        //悲观锁 解决并发的问题
        Card card = cardMapper.getById4Lock(cardId);
        if (null == card || !card.getPassword().equals(password)) {
            throw new BizException("卡号不存在或者密码错误");
        }

        //卡的所属人和当前用户是否一致
        if (card.getUserId() != userId) {
            throw new BizException("无权操作");
        }

        //校验金额 可能最小存款金额
        if (Double.parseDouble(amount) <= 0) {
            throw new BizException("存款金额必须大于零");
        }

        //修改语句，sql语句不能使用大而全的更新sql，不利于维护。
        //修改某张银行卡余额，有可能会出现并发的问题，导致了余额不对。
        plusBlance(card, amount, FlowTypeEnum.DEPOSIT);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void draw(long cardId, String password, String amount, long userId) {
        if (StringUtils.isBlank(password) ||
                StringUtils.isBlank(amount)) {
            throw new BizException("有必填参数为空");
        }

        Card card = cardMapper.getById(cardId);
        if (null == card || !card.getPassword().equals(password)) {
            throw new BizException("卡号不存在或者密码错误");
        }

        //卡的所属人和当前用户是否一致
        if (card.getUserId() != userId) {
            throw new BizException("无权操作");
        }

        //校验金额 可能最小存款金额
        if (Double.parseDouble(amount) <= 0) {
            throw new BizException("取款金额必须大于零");
        }

        //乐观锁 通过版本号对比 或者 新旧两个值对比
        //乐观锁也是通过比较和交换来达到并发修改的问题  CAS(compare And Swap)
        //乐观锁如果使用的是新旧两个值比较，有可能会出现ABA的问题，如何解决？
        //可以采用版本号version，每次更新都加1,version不会重复，就不会出现ABA的问题。
        subBalance(card, amount, FlowTypeEnum.DRAW);
    }

    /**
     * 转账
     * @param cardId
     * @param password
     * @param amount
     * @param cardNumIn
     * @param userId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void transfer(long cardId, String password, String amount, String cardNumIn, long userId) {
        if (StringUtils.isBlank(password) ||
                StringUtils.isBlank(amount) ||
                StringUtils.isBlank(cardNumIn)) {
            throw new BizException("有必填参数为空");
        }

        Card cardOut = cardMapper.getById(cardId);
        Card cardIn = cardMapper.getByCardNum(cardNumIn);
        if (null == cardOut || null == cardIn) {
            throw new BizException("银行卡不存在或者密码错误");
        }

        //数组转集合
        List<Card> cards = Arrays.asList(cardOut, cardIn);
        //集合排序
        Collections.sort(cards, new CardCompartor());

        //排好序后，对其进行加锁 也就是悲观锁
        for (Card card : cards) {
            cardMapper.getById4Lock(card.getId());
        }

        //悲观锁，有可能会出现死锁的问题，比如两个账号互相转账，就有可能出现死锁，如何解决？
        //在锁的时候，先对银行卡排序，按照顺序进行加锁，即可。

        //转账收入  悲观锁  如果按照转出和转入的顺序加锁，可能会产生死锁的问题
//        Card cardIn = cardMapper.getByNum4Lock(cardNumIn);
//
//        //悲观锁
//        Card cardOut = cardMapper.getById4Lock(cardId);


        if (null == cardOut || !cardOut.getPassword().equals(password)) {
            throw new BizException("卡号不存在或者密码错误");
        }

        if (userId != cardOut.getUserId()) {
            throw new BizException("无权操作");
        }

        //校验金额  转账金额的范围 限额的范围
        if (Double.parseDouble(amount) <= 0){
            throw new BizException("转账金额必须大于零");
        }

        //修改余额，添加流水
        subBalance(cardOut, amount, FlowTypeEnum.TRANSFER_OUT);


        if (null == cardIn) {
            throw new BizException("转入卡号不存在");
        }

        //修改余额，添加流水
        plusBlance(cardIn, amount, FlowTypeEnum.TRANSFER_IN);
    }


    protected void plusBlance(Card card, String amount, FlowTypeEnum flowTypeEnum) {
        int newBalance = card.getBalance() + MoneyUtil.yuanTofen(amount);
        //乐观锁
        int rows = cardMapper.updateBalance(card.getId(), newBalance, card.getBalance());
        if (1 != rows) {
            throw new BizException("修改余额失败");
        }
        insertFlow(amount, card, flowTypeEnum);
    }

    protected void subBalance(Card card, String amount, FlowTypeEnum flowTypeEnum) {
        //检查余额
        if (card.getBalance() < MoneyUtil.yuanTofen(amount)) {
            throw new BizException("余额不足，请充值");
        }

        int newBalance = card.getBalance() - MoneyUtil.yuanTofen(amount);
        //乐观锁
        int rows = cardMapper.updateBalance(card.getId(), newBalance, card.getBalance());
        if (1 != rows) {
            throw new BizException("修改余额失败");
        }

        insertFlow(amount, card, flowTypeEnum);
    }

    private void insertFlow(String amount, Card card, FlowTypeEnum flowTypeEnum) {
        //流水
        Flow flow = new Flow();
        if (flowTypeEnum == FlowTypeEnum.DRAW || flowTypeEnum == FlowTypeEnum.TRANSFER_OUT) {
            flow.setAmount(-MoneyUtil.yuanTofen(amount));
        } else {
            flow.setAmount(MoneyUtil.yuanTofen(amount));
        }

        flow.setCardId(card.getId());
        flow.setCreateTime(new Date());
        flow.setFlowType(flowTypeEnum.getK());
        flow.setRemark(flowTypeEnum.getV());
        flow.setUserId(card.getUserId());

        flowMapper.insert(flow);
    }


    public List<String> createlist(int age) {
        if (age > 20) {
            List<String> objects = new ArrayList<>();
            objects.add("tom");
            objects.add("jack");
            return objects;
        }

//        return null;//不推荐 如意引起NPE异常 2B程序员

//        return new ArrayList<>();//返回一个空集合  不错，有进步；普通程序员
        return Collections.EMPTY_LIST;//推荐 优秀程序员 不用每次都创建空对象，用集合工具类提供的空集合常量，节省内存。
    }


    @Override
    public List<CardDTO> listMyCard(long userId) {
        //当查不到，返回的不是null，是一个空集合
        List<Card> cards = cardMapper.listCardByUserId(userId);
        if (cards.isEmpty()) {
            return Collections.EMPTY_LIST;
        }

        List<CardDTO> dtos = new ArrayList<>(cards.size());
        for (Card card : cards) {
            CardDTO cardDTO = new CardDTO();
            dtos.add(cardDTO);

            cardDTO.setCardNum(CardUtil.formatNum(card.getCardNum()));
            cardDTO.setCardId(card.getId());
            cardDTO.setAmount(MoneyUtil.fenToYuan(card.getBalance()));
        }

        return dtos;
    }
}
